
#include <BImage>
#include <BScene>
#include <BMaterial>

#include "ReadWrite_stl.h"

ReadWrite_stl::ReadWrite_stl()
{
	_author = "zym";
	_version = "1.0";
	_description = "This is BWE Plugin for reading *.jpg image file, write width libjpeg, thank a lot !";
}
ReadWrite_stl::~ReadWrite_stl()
{

}

struct FaceInfo
{
	BVectorf normal;
	BVectorf v0;
	BVectorf v1;
	BVectorf v2;
	short def;
};

BObject* ReadWrite_stl::load(const BString& fileName)
{
	BString name = fileName.name();
	name.remove(name.find('.'), name.size());
	BMesh* mesh = new BMesh(name);
	if (!load(fileName, mesh))
	{
		delete mesh;
		return 0;
	}
	return mesh;
}
bool ReadWrite_stl::load(const BString& fileName, BObject* object)
{
	BScene* scene = dynamic_cast<BScene*>(object);
	BNode* node = dynamic_cast<BNode*>(object);
	BMesh* mesh = dynamic_cast<BMesh*>(object);
	if (!scene && !node && !mesh)
		return false;

	_mates.clear();
	if (!_file.open(fileName, IO_Read))
	{
		puts("file open failed.");
		return false;
	}

	char name[80] = { 0 };
	_file.read(name, 80);
	name[79] = 0;
	int size = 0;
	_file.read(size);

	BVectorArray* vertices = new BVectorArray(size * 3);
	BVectorArray* normals = new BVectorArray(size * 3);

	FaceInfo info;
	for (int i = 0; i < size; i++)
	{
		int pos = i * 3;
		_file.read((void*)&info, 50);
		(*vertices)[pos + 0] = info.v0;
		(*vertices)[pos + 1] = info.v1;
		(*vertices)[pos + 2] = info.v2;
		(*normals)[pos + 0] = info.normal;
		(*normals)[pos + 1] = info.normal;
		(*normals)[pos + 2] = info.normal;
	}

	if (!mesh)
		mesh = new BMesh();
	BString fname = fileName.name();
	fname -= fileName.ext();
	fname -= '.';
	mesh->setName(fname);
	mesh->setVertices(vertices);
	mesh->setNormals(normals);

	if (scene)
		scene->insert(mesh);

	if (node)
		node->insertShape(mesh);

	_file.close();
	return true;
}
bool ReadWrite_stl::save(const BString& fileName, const BObject* object) const
{
	if (!object)
		return false;

	const BMesh* mesh = dynamic_cast<const BMesh*>(object);
	if (!mesh)
	{
		if (const BNode* node = dynamic_cast<const BNode*>(object))
			mesh = dynamic_cast<const BMesh*>(node->shape(0));
	}
	if (!mesh)
	{
		if (const BGroup* group = dynamic_cast<const BGroup*>(object))
			mesh = dynamic_cast<const BMesh*>(group->shape(0));
	}
	if (!mesh)
	{
		if (const BScene* scene = dynamic_cast<const BScene*>(object))
			mesh = dynamic_cast<const BMesh*>(scene->shape(0));
	}
	if (!mesh)
		return false;

	if (!_file.open(fileName, IO_Write))
		return false;

	const BVectorArray* vertices = mesh->vertices();
	const BVectorArray* normal = mesh->normals();
	const BFaceArray* faces = mesh->faces();
	if (vertices && normal && faces)
	{
		char name[80];
		memcpy(name, mesh->name().cstr(), mesh->name().size() + 1);
		_file.write(name, 80);
		int size = faces->size();
		_file.write(size);
		FaceInfo info;
		for (int i = 0; i < size; i++)
		{
			int pos = i * 3;
			info.v0 = vertices->at(pos + 0);
			info.v1 = vertices->at(pos + 1);
			info.v2 = vertices->at(pos + 2);
			info.normal = (info.v1 - info.v0).cross(info.v2 - info.v0);
			info.normal.normalize();
			_file.write(&info, 50);
		}
	}

	return true;
}

BObject* ReadWrite_stl::read(const BBuffer& buffer)
{
	BMesh* mesh = new BMesh();
	if (!read(buffer, mesh))
	{
		delete mesh;
		return 0;
	}
	return mesh;
}
bool ReadWrite_stl::read(const BBuffer& buffer, BObject* object)
{
	BScene* scene = dynamic_cast<BScene*>(object);
	BNode* node = dynamic_cast<BNode*>(object);
	BMesh* mesh = dynamic_cast<BMesh*>(object);
	if (!scene && !node && !mesh)
		return false;

	_mates.clear();
	if (buffer.empty() || buffer.mode() & IO_Text)
		return 0;
	buffer.seek(0);

	char name[80];
	int size = 0;
	buffer.read(name, 80);
	buffer.read(&size, 4);

	BVectorArray* vertices = new BVectorArray(size * 3);
	BVectorArray* normals = new BVectorArray(size * 3);

	FaceInfo info;
	for (int i = 0; i < size; i++)
	{
		int pos = i * 3;
		buffer.read(&info, 50);
		(*vertices)[pos + 0] = info.v0;
		(*vertices)[pos + 1] = info.v1;
		(*vertices)[pos + 2] = info.v2;
		(*normals)[pos + 0] = info.normal;
		(*normals)[pos + 1] = info.normal;
		(*normals)[pos + 2] = info.normal;
	}

	if (!mesh)
		mesh = new BMesh();
	mesh->setName(name);
	mesh->setVertices(vertices);
	mesh->setNormals(normals);

	if (scene)
		scene->insert(mesh);

	if (node)
		node->insertShape(mesh);

	return true;
}
bool ReadWrite_stl::write(BBuffer& buffer, const BObject* object) const
{
	if (!object)
		return false;

	const BMesh* mesh = dynamic_cast<const BMesh*>(object);
	if (!mesh)
	{
		if (const BNode* node = dynamic_cast<const BNode*>(object))
			mesh = dynamic_cast<const BMesh*>(node->shape(0));
	}
	if (!mesh)
	{
		if (const BGroup* group = dynamic_cast<const BGroup*>(object))
			mesh = dynamic_cast<const BMesh*>(group->shape(0));
	}
	if (!mesh)
	{
		if (const BScene* scene = dynamic_cast<const BScene*>(object))
			mesh = dynamic_cast<const BMesh*>(scene->shape(0));
	}
	if (!mesh)
		return false;

	if (buffer.empty() || buffer.mode() & IO_Text)
		return false;
	buffer.seek(0);

	const BVectorArray* vertices = mesh->vertices();
	const BVectorArray* normal = mesh->normals();
	const BFaceArray* faces = mesh->faces();
	if (vertices && normal && faces)
	{
		char name[80];
		memcpy(name, mesh->name().cstr(), mesh->name().size() + 1);
		buffer.write(name, 80);
		int size = faces->size();
		buffer.write(size);
		FaceInfo info;
		for (int i = 0; i < size; i++)
		{
			int pos = i * 3;
			info.v0 = vertices->at(pos + 0);
			info.v1 = vertices->at(pos + 1);
			info.v2 = vertices->at(pos + 2);
			info.normal = (info.v1 - info.v0).cross(info.v2 - info.v0);
			info.normal.normalize();
			buffer.write(&info, 50);
		}
	}

	return true;
}

void ReadWrite_stl::setOptions(const BString& options)
{
	_options = options;
}
const BString& ReadWrite_stl::options() const
{
	return _options;
}

const BString& ReadWrite_stl::author() const
{
	return _author;
}
const BString& ReadWrite_stl::version() const
{
	return _version;
}
const BString& ReadWrite_stl::description() const
{
	return _description;
}
const BString& ReadWrite_stl::message() const
{
	return _message;
}

MateStl* ReadWrite_stl::findMaterial(const BString& matName)
{
	for (BList<MateStlHolder>::iterator it = _mates.begin(); it != _mates.end(); ++it)
	{
		MateStl* mate = it->ptr();
		BMaterial* material = mate->material;
		if (material->name() == matName)
			return mate;
	}
	return 0;
}

